package com.cch.config;

import com.cch.filter.JwtAuthenticationTokenFilter;
import com.cch.security.PhoneNumAuthenticationFilter;
import com.cch.security.PhoneNumAuthenticationProvider;
import com.cch.security.handle.AuthenticationEntryPointImpl;
import com.cch.service.UserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;

/**
 * @author cch
 * @date 2025年05月21日 9:31
 * WebSecurityConfigurerAdapter 过期了，在目前最新的 Spring Security6.1 中，这个类已经完全被移除了，想凑合着用都不行了。
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {

    @Autowired
    public UserDetailService userDetailService;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;


    //AuthenticationManager  的获取，以前可以通过重写父类的方法来获取这个 Bean，类似下面这样：
//    @Bean
//    public AuthenticationManager authenticationManagerBean() throws Exception {
//        return super.authenticationManagerBean();
//    }


    // 以后就只能自己创建这个 Bean 了，类似下面这样：
    @Bean
    public AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        PhoneNumAuthenticationProvider smsAuthenticationProvider = new PhoneNumAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailService);
        smsAuthenticationProvider.setUserDetailService(userDetailService);
        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider, smsAuthenticationProvider);
//        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        return providerManager;
    }
    //当然，也可以从 HttpSecurity 中提取出来 AuthenticationManager，如下：
    //AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
    //authenticationManagerBuilder.userDetailsService(userDetailsService);
//    authenticationManagerBuilder.authenticationProvider(weChatAuthenticationProvider());
    //authenticationManager = authenticationManagerBuilder.build();

    /**
     * 定义securityContextRepository，加入两种securityContextRepository,5.8以后需要手动配置
     */
    @Bean
    public SecurityContextRepository securityContextRepository(){
        HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
        DelegatingSecurityContextRepository defaultRepository = new DelegatingSecurityContextRepository(
                httpSecurityRepository, new RequestAttributeSecurityContextRepository());
        return defaultRepository;
    }


    @Bean
    public PhoneNumAuthenticationFilter phoneNumAuthenticationFilter() throws Exception {
        // 手机号码拦截器, 获取手机号码
        PhoneNumAuthenticationFilter phoneNumAuthenticationFilter = new PhoneNumAuthenticationFilter();
        phoneNumAuthenticationFilter.setAuthenticationManager(authenticationManager());
        phoneNumAuthenticationFilter.setSecurityContextRepository(securityContextRepository());
        //使用手机号登录失败了如何处理
//        phoneNumAuthenticationFilter.setAuthenticationFailureHandler(customizeAuthencationFailureHandler);
        return phoneNumAuthenticationFilter;
    }


    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // 所有请求都需要认证
                .authorizeHttpRequests(auth->auth.requestMatchers("/login/**").permitAll()
                        .anyRequest().authenticated())
                .exceptionHandling(e -> e.authenticationEntryPoint(new AuthenticationEntryPointImpl()))
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 禁用csrf，因为登录和登出是post请求，csrf会屏蔽掉post请求
                .csrf(csrf -> csrf.disable())
                // 添加到过滤器链路中，确保在AuthorizationFilter过滤器之前
                .addFilterAfter(phoneNumAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                //默认配置.formLogin(Customizer.withDefaults())
                //现在采用的是单独写LoginController进行登录，手动调用进行验证，所以这里关闭了，关闭后，默认的/login路径将不能进行访问，自定义的登录授权不受影响
                .formLogin(login -> login.disable())
                //也可以采用也可以使用fromLogin的成功successHandler进行登录成功后发放token，放弃自定义LoginController，手动调用权限认证，不会执行登录成功和失败的Handler
                //formLogin配置以后，默认的/login登录认证将启用
//                .formLogin(login -> login.successHandler(new PhoneNumLoginSuccessHandler()))
                // 设置全局authenticationManager
                .authenticationManager(authenticationManager())
                // 设置全局securityContextRepository
                .securityContext(c->c.securityContextRepository(securityContextRepository()));
        return http.build();
    }



//    @Bean
//    public WebSecurityCustomizer webSecurityCustomizer() {
//        return web -> web.ignoring().requestMatchers("/html/login","/phone","/login/**");
//    }


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


}
